package com.by.toolkit.utils;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * 描述：
 *
 * @author liujun
 * @date 2023年07月26 11:43 AM
 */
public final class ByOptional<T> {

    private static final ByOptional<?> EMPTY = new ByOptional();

    private final T value;

    private ByOptional() {
        this.value = null;
    }

    public static <T> ByOptional<T> empty() {
        return (ByOptional<T>) EMPTY;
    }

    private ByOptional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> ByOptional<T> of(T value) {
        return new ByOptional(value);
    }

    public static <T> ByOptional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    /**
     * 增强方法1：做一些事情并返回原值yxoption
     */
    public ByOptional<T> ifPresentPeek(Consumer<? super T> action) {
        if (this.value != null) {
            action.accept(this.value);
        }
        return this;
    }


    /**
     * 增强3：做些事情并返回原值
     */
    public ByOptional<T> peek(Consumer<? super T> action) {
        action.accept(this.value);
        return this;
    }

    /**
     * 增强方法2：做一些事情并返回原值yxoption
     */
    public ByOptional<T> ifNotPresentPeek(Runnable runnable) {
        if (this.value == null) {
            runnable.run();
        }
        return this;
    }

    /**
     * 增强方法4：判断match
     */
    public boolean match(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!this.isPresent()) {
            return false;
        }
        return predicate.test(this.value);
    }


    public T get() {
        if (this.value == null) {
            throw new NoSuchElementException("No value present");
        } else {
            return this.value;
        }
    }

    public boolean isPresent() {
        return this.value != null;
    }

    public boolean isEmpty() {
        return this.value == null;
    }

    public void ifPresent(Consumer<? super T> action) {
        if (this.value != null) {
            action.accept(this.value);
        }
    }

    public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (this.value != null) {
            action.accept(this.value);
        } else {
            emptyAction.run();
        }

    }

    public ByOptional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!this.isPresent()) {
            return this;
        } else {
            return predicate.test(this.value) ? this : empty();
        }
    }


    public <U> ByOptional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        return !this.isPresent() ? empty() : ofNullable(mapper.apply(this.value));
    }

    public <U> ByOptional<U> flatMap(Function<? super T, ? extends ByOptional<? extends U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!this.isPresent()) {
            return empty();
        } else {
            ByOptional<U> r = (ByOptional)mapper.apply(this.value);
            return (ByOptional)Objects.requireNonNull(r);
        }
    }

    public ByOptional<T> or(Supplier<? extends ByOptional<? extends T>> supplier) {
        Objects.requireNonNull(supplier);
        if (this.isPresent()) {
            return this;
        } else {
            ByOptional<T> r = (ByOptional)supplier.get();
            return (ByOptional)Objects.requireNonNull(r);
        }
    }

    public Stream<T> stream() {
        return !this.isPresent() ? Stream.empty() : Stream.of(this.value);
    }

    public T orElse(T other) {
        return this.value != null ? this.value : other;
    }



    public T orElseGet(Supplier<? extends T> supplier) {
        return this.value != null ? this.value : supplier.get();
    }

    public T orElseThrow() {
        if (this.value == null) {
            throw new NoSuchElementException("No value present");
        } else {
            return this.value;
        }
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (this.value != null) {
            return this.value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof ByOptional)) {
            return false;
        } else {
            ByOptional<?> other = (ByOptional)obj;
            return Objects.equals(this.value, other.value);
        }
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(this.value);
    }

    @Override
    public String toString() {
        return this.value != null ? String.format("ByOptional[%s]", this.value) : "ByOptional.empty";
    }

    public static void main(String[] args) {
        ByOptional.of(1L).ifPresent(System.out::println);
    }
}

